<template>
  <el-form ref="form" v-if="part && part.model" :model="part.model" class="z-data-form"><slot></slot></el-form>
</template>

<script>
import {createFormModel} from "../../../zform/form";
import {createFieldMixin} from "../../../zform/cm-field-core";
import {buildVueComponentMan} from "../../../zform/main";
const PREFIX = 'comformscr3__'

function flattenObject(ob) {
  var toReturn = {};

  for (var i in ob) {
    // eslint-disable-next-line no-prototype-builtins
    if (!ob.hasOwnProperty(i)) continue;

    if ((typeof ob[i]) == 'object' && ob[i] !== null) {
      var flatObject = flattenObject(ob[i]);
      for (var x in flatObject) {
        // eslint-disable-next-line no-prototype-builtins
        if (!flatObject.hasOwnProperty(x)) continue;

        toReturn[i + '.' + x] = flatObject[x];
      }
    } else {
      toReturn[i] = ob[i];
    }
  }
  return toReturn;
}

export default {
  name: 'ZDataGridForm',
  setup(props, ctx) {
    // console.log(ctx)
    const {provide, reactive, getCurrentInstance, watch, inject, toRaw} = globalThis.vueCompositionAPI
    let instance = getCurrentInstance()
    let CustomDymComponent = class  extends buildVueComponentMan(globalThis.ZCACHE_VUE) {};
    CustomDymComponent.app = globalThis.ZCACHE_VUE
    // console.log(CustomDymComponent)
    let part = {}

    let config = {}

    let JSON5 = ZY.JSON5;
    let lodash = ZY.lodash;

    let cmFields = new Map()
    let cmRefsMap = new Map()


    let locked = true;
    let uuid = PREFIX + ZY.rid(10);
    /**
     * 存放配置里有computedFun的属性
     * @type {Map<any, any>}
     */
    let watchHandleMap = new Map();
    function registerWatchHandle(key, value) {
      watchHandleMap.set(key, value)
    }

    /**
     * 初始化
     * @param partDef
     * @returns {{model: UnwrapRef<{}>}}
     */
    function initPart(partDef) {
      let cachedModel = '{}';
      let rowDef = partDef.def;
      let obj = createFormModel(rowDef) ?? {}
      // console.log(obj, rowDef)
      let model = reactive(obj);
      let modelLocks = false;
      // console.log('initPart', model)

      watch(model, (newVal, oldVal) => {
        let newObj = JSON5.parse(JSON5.stringify(newVal));
        // console.log(newObj)
        let objKeys = Object.keys(newObj)
        let oldObj = JSON5.parse(cachedModel)
        let diffed = ZY.diff(oldObj, newObj);
        let flattenD = flattenObject(diffed);

        if (!modelLocks) {
          // console.log('watch', flattenD)
          let flattenDKeys = Object.keys(flattenD)
          watchHandleMap.forEach(function (item, key) {
            let isContains = lodash.difference(key, objKeys).length < 1;
            let flattenDHas = lodash.difference(flattenDKeys, key).length < 1;
            // console.log(flattenDHas, key, flattenDKeys)
            if (isContains && flattenDHas) {
              console.log('changed', key, )
              modelLocks = true
              item.run(model);
              setTimeout(() => {
                modelLocks = false
              }, 300)
            }
            // console.log(objKeys, key, isContains)
          })

        }

        cachedModel = JSON5.stringify(newObj)
      })

      return {
        model
      }
    }

    part = initPart({def: {}})

    let registerHookMap = new Map()
    function registerHook(name, key, value) {
      let map = registerHookMap.get(name)
      if (!map) {
        map = new Map()
      }
      map.set(key, value)
      registerHookMap.set(name, map)
    }

    function unRegisterHook(name, key) {
      let map = registerHookMap.get(name)
      if (!map) {
        return
      }
      map.delete(key)
      registerHookMap.set(name, map)
    }


    function getCmFieldContext(key) {
      let def = cmFields.get(key)
      if (def) {
        return def.getWidgetContext()
      }
    }

    function afterUpdateValue(partName, model, s_path, pathArr, e) {
      // console.log('updateValue', model, s_path, pathArr, e)
      let afterUpdateHookMap = registerHookMap.get('afterUpdate')
      if (afterUpdateHookMap) {
        // let afterUpdateHook = afterUpdateHookMap.get(s_path)
        // console.log(afterUpdateHook, afterUpdateHookMap, s_path)
        // if (afterUpdateHook) {
        //   afterUpdateHook({partName, model, s_path, pathArr, getCmFieldContext, cmFields, e})
        // }

        afterUpdateHookMap.forEach(afterUpdateHook => {
          afterUpdateHook({partName, model, s_path, pathArr, getCmFieldContext, cmFields, e, ctx})
        })
      }
      // console.log(zformLoader)
      ctx.emit('updateValue', {model, s_path, pathArr, e})
    }

    function getUI_CONFIG(path) {
      // console.log(path)
      if (typeof path === 'undefined') {
        return config
      }
      return lodash.get({
        config
      }, path)
    }

    function updateValue(partName = '', pathArr = [], newVal) {
      let s_path = ZY.getObjPathFromPathArr(pathArr);
      let model = part.model;
      lodash.set(model, s_path, newVal);
      afterUpdateValue(partName, model, s_path, pathArr, newVal)
    }

    // function updateArrValue(partName = '', pathArr = [], e) {
    //   // let s_path = ZY.getObjPathFromPathArr(pathArr);
    //   // let model = parts[partName].model;
    //   // console.log('updateArrValue', partName, model, s_path, e)
    //   updateValue(partName, pathArr, e)
    // }

    /**
     * 注册cm-field
     * @param def
     * @param key
     * @param value
     */
    function register(def, key, value) {
      cmFields.set(key, value)
      CustomDymComponent.register(def)
      // console.log(cmFields)
      if (value?.props?.ui?.ref) {
        cmRefsMap.set(value.props.ui.ref, value)
      }
    }

    /**
     * 解除cm-field注册
     * @param def
     * @param key
     */
    function unRegister(def, key) {
      cmFields.delete(key)
      CustomDymComponent.unRegister(def)
    }


    function listenEvent(...args) {
      // console.log(args)
      // if (listener) {
      //   listener(...args)
      // }
    }

    function getDataSync() {
      return toRaw(part.model)
    }

    function getRawModel() {
      return toRaw(part.model)
    }

    function setData(d = {}) {
      Object.entries(d).forEach(([key, value]) => {
        let ctx = getCmFieldContext(key)
        if (ctx) {
          ctx.zfield__setAndUpdate(value)
        }
        // console.log(key, ctx)
      })
    }


    /**
     * 导入数据
     * @param data
     * @returns {Promise<void>}
     */
    async function importData(data = {}) {
      if (data) {
        locked = false
        // parts[partName].model = data
        let model = part.model
        // console.log('importData', data, part.model)
        ZY.lodash.each(data, function (item, key) {
          model[key] = item
        })
        setTimeout(() => {
          locked = true
        }, 30)
      }
    }

    async function submit() {
      // console.log(instance.refs)
      let self = this;
      return new Promise((resolve, reject) => {
        let form = instance?.refs?.form;
        if (!form) {
          reject(new Error('not form'))
        }
        // form.resetFields()
        form.validate(function (isValid, errors = []) {
          // console.log(isValid)
          resolve([isValid, errors, getDataSync()])
        });
      })
    }


    let exportCtx = {

    }

    let comIns = {
      uuid,
      getUI_CONFIG,
      register,
      getRawModel,
      registerHook,
      unRegisterHook,
      setData,
      exportCtx,
      importData,
      part,
      unRegister,
      submit,
      listenEvent,
      getDataSync,
      updateValue,
      fieldMixin: createFieldMixin()
    }

    provide('curFormCon', comIns)

    return comIns
  }
}
</script>
